﻿// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

#region using

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Kinect;
using Coding4Fun.Kinect.Wpf;
using KinectControls;
using Kinect.Toolbox;


using Awesomium.Core;
using Awesomium.Windows;
using Awesomium.Windows.Controls;
using System.IO;

#endregion

namespace RITInterativeMap
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region constructor / state
        
        public MainWindow()
        {
            InitializeComponent();
        }

        bool closing = false;
        const int skeletonCount = 6; 
        Skeleton[] allSkeletons = new Skeleton[skeletonCount];
        string mapType = "ROADMAP";
        const int PAN_DIST = 75;
        const int SWP_PAN_DIST = 150;
        const int FAST_BUTTON_TIME = 1;
        private CustomSwipeDetector swipeLeftHandGD;
        private CustomSwipeDetector swipeRightHandGD;
        private string menuSide = "LEFT";
        private HandCursor selectingHand;

        #endregion

        #region Initialization of Kinect

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            kinectSensorChooser.KinectSensorChanged += new DependencyPropertyChangedEventHandler(kinectSensorChooser1_KinectSensorChanged);
            initMap();
            selectingHand = leftHand;
            leftHand.SetLeftBlack();
            menu.injectHands(leftHand, rightHand);
            menu.injectMapView(RIT_Map);
            menu.buildMenu();
            menu.injectZoomSlider(zoomLevel);
            setFastSelect();
            initGestures();

            leftMenuSideButton.highlightButton();
            streetSettingButton.highlightButton();
        }

        private void initMap()
        {
        
            String curDir = Directory.GetCurrentDirectory();

            // Console.WriteLine("@@@@@@@@@@@@@@@ curDir: " + curDir);
            string mapFile = "file:///" + curDir + @"\maps\index2.html";
            // Console.WriteLine("@@@@@@@@@@@ mapFile: " + mapFile);
            RIT_Map.LoadURL(mapFile); 
        }

        private void initFiddler()
        {
            Fiddler.FiddlerApplication.BeforeRequest += delegate(Fiddler.Session os)
            {
                os.bBufferResponse = true;
            };
            Fiddler.FiddlerApplication.BeforeResponse += delegate(Fiddler.Session os)
            {
                if(os.fullUrl.Contains("maps.rit.edu"))
                    os.oResponse.headers.Add("Access-Control-Allow-Origin", "*");
            };
            Fiddler.FiddlerApplication.Startup(8877, true, true);
        }

        private void initGestures()
        {
            swipeLeftHandGD = new CustomSwipeDetector();
            swipeRightHandGD = new CustomSwipeDetector();
            swipeLeftHandGD.OnGestureDetected += new Action<string>(swipe_OnGestureDetected);
            swipeRightHandGD.OnGestureDetected += new Action<string>(swipe_OnGestureDetected);
        }
        
        private void setFastSelect()
        {
            panUp.HoverTime = FAST_BUTTON_TIME;
            panDown.HoverTime = FAST_BUTTON_TIME;
            panLeft.HoverTime = FAST_BUTTON_TIME;
            panRight.HoverTime = FAST_BUTTON_TIME;

            zoomIn.HoverTime = FAST_BUTTON_TIME;
            zoomOut.HoverTime = FAST_BUTTON_TIME;
        }

        void kinectSensorChooser1_KinectSensorChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            KinectSensor old = (KinectSensor)e.OldValue;

            StopKinect(old);

            KinectSensor sensor = (KinectSensor)e.NewValue;

            if (sensor == null)
            {
                return;
            }

            


            var parameters = new TransformSmoothParameters
            {
                Smoothing = 0.05f,
                Correction = 0.0f,
                Prediction = 0.0f,
                JitterRadius = 1.0f,
                MaxDeviationRadius = 0.5f
            };
            sensor.SkeletonStream.Enable(parameters);

            //sensor.SkeletonStream.Enable();

            sensor.AllFramesReady += new EventHandler<AllFramesReadyEventArgs>(sensor_AllFramesReady);
            sensor.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30); 
            sensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);

            //leftHand.SetLeft();
            

            try
            {
                sensor.Start();
                initFiddler();
            }
            catch (System.IO.IOException)
            {
                kinectSensorChooser.AppConflictOccurred();
            }
            
        }

        #endregion

        #region Kinect Checks

        void sensor_AllFramesReady(object sender, AllFramesReadyEventArgs e)
        {
            if (closing)
            {
                return;
            }

            //Get a skeleton
            Skeleton first =  GetFirstSkeleton(e);

            if (first == null)
            {
                return; 
            }

            GetCameraPoint(first, e); 

            //set scaled position
            ScalePosition(leftHand, first.Joints[JointType.HandLeft]);
            ScalePosition(rightHand, first.Joints[JointType.HandRight]);

            Check_Home_Button(selectingHand);

            if (settingsMenu.Visibility == Visibility.Visible)
            {
                leftMenuSideButton.Check(selectingHand);
                rightMenuSideButton.Check(selectingHand);
                streetSettingButton.Check(selectingHand);
                satelliteSettingButton.Check(selectingHand);
            }
            else
            {
                menu.Check_Hover_Buttons(selectingHand);
            }

            settingsButton.Check(selectingHand);
            Check_Zoom_Buttons(selectingHand);
            Check_Pan_Buttons(selectingHand);
        }

        #endregion

        #region Checking and scaling Hands

        void GetCameraPoint(Skeleton first, AllFramesReadyEventArgs e)
        {

            using (DepthImageFrame depth = e.OpenDepthImageFrame())
            {
                if (depth == null ||
                    kinectSensorChooser.Kinect == null)
                {
                    return;
                }

                //Map a joint location to a point on the depth map
                //left hand
                DepthImagePoint leftDepthPoint =
                    depth.MapFromSkeletonPoint(first.Joints[JointType.HandLeft].Position);
                //right hand
                DepthImagePoint rightDepthPoint =
                    depth.MapFromSkeletonPoint(first.Joints[JointType.HandRight].Position);


                //Map a depth point to a point on the color image
                //left hand
                ColorImagePoint leftColorPoint =
                    depth.MapToColorImagePoint(leftDepthPoint.X, leftDepthPoint.Y,
                    ColorImageFormat.RgbResolution640x480Fps30);
                //right hand
                ColorImagePoint rightColorPoint =
                    depth.MapToColorImagePoint(rightDepthPoint.X, rightDepthPoint.Y,
                    ColorImageFormat.RgbResolution640x480Fps30);

                swipeRightHandGD.Add(first.Joints[JointType.HandRight].Position, kinectSensorChooser.Kinect);
                swipeLeftHandGD.Add(first.Joints[JointType.HandLeft].Position, kinectSensorChooser.Kinect);

                //Set location
                CameraPosition(leftHand, leftColorPoint);
                CameraPosition(rightHand, rightColorPoint);
            }        
        }


        Skeleton GetFirstSkeleton(AllFramesReadyEventArgs e)
        {
            using (SkeletonFrame skeletonFrameData = e.OpenSkeletonFrame())
            {
                if (skeletonFrameData == null)
                {
                    return null; 
                }

                
                skeletonFrameData.CopySkeletonDataTo(allSkeletons);

                //get the first tracked skeleton
                Skeleton first = (from s in allSkeletons
                                         where s.TrackingState == SkeletonTrackingState.Tracked
                                         select s).FirstOrDefault();
                return first;

            }
        }

        private void CameraPosition(FrameworkElement element, ColorImagePoint point)
        {
            //Divide by 2 for width and height so point is right in the middle 
            // instead of in top/left corner
            Canvas.SetLeft(element, point.X - element.Width / 2);
            Canvas.SetTop(element, point.Y - element.Height / 2);

        }

        private void ScalePosition(FrameworkElement element, Joint joint)
        {
            //convert the value to X/Y
            //Joint scaledJoint = joint.ScaleTo(1280, 720); 

            //convert & scale (.3 = means 1/3 of joint distance
            // Scale joints to screen size
            Joint scaledJoint = joint.ScaleTo( (int) MainCanvas.ActualWidth, 
                (int) MainCanvas.ActualHeight, .3f, .3f);

            Canvas.SetLeft(element, scaledJoint.Position.X);
            Canvas.SetTop(element, scaledJoint.Position.Y);

        }

        #endregion

        #region Closing application

        private void StopKinect(KinectSensor sensor)
        {
            if (sensor != null)
            {
                if (sensor.IsRunning)
                {
                    //stop sensor 
                    sensor.Stop();

                    //stop audio if not null
                    if (sensor.AudioSource != null)
                    {
                        sensor.AudioSource.Stop();
                    }


                }
            }
            Fiddler.FiddlerApplication.Shutdown();
        }


        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            closing = true; 
            StopKinect(kinectSensorChooser.Kinect); 
        }

        #endregion

        #region button checks
        private void Check_Home_Button(KinectControls.HandCursor hand)
        {
            homeButton.Check(hand);
        }

        private void Check_Zoom_Buttons(KinectControls.HandCursor hand)
        {
            zoomIn.Check(hand);
            zoomOut.Check(hand);
        }

        private void Check_Pan_Buttons(KinectControls.HandCursor hand)
        {
            panUp.Check(hand);
            panDown.Check(hand);
            panLeft.Check(hand);
            panRight.Check(hand);
        }

        #endregion

        #region event listeners

        private void homeButton_Click(object sender, EventArgs e)
        {
            if (settingsMenu.Visibility == Visibility.Visible)
            {
                settingsMenu.Visibility = Visibility.Collapsed;

                menu.Visibility = Visibility.Visible;

                settingsMenuGrid.Visibility = Visibility.Visible;
            }

            menu.homeClicked();
        }

        private void settingsButton_Click(object sender, EventArgs e)
        {
            menu.Visibility = Visibility.Collapsed;

            settingsMenu.Visibility = Visibility.Visible;

            settingsMenuGrid.Visibility = Visibility.Collapsed;
        }

        /**
        * Switches the UI Elements in the grid and adjusts the grid size
        * Works as a toggle so that one action can be used regardless of current hand
        * Does not switch the kinectSensorChooser
        */

        private void leftMenuSide_Click(object sender, EventArgs e)
        {
            if (menuSide == "LEFT")
            {
                return;
            }
            else
            {
                toggleMenuSide();
                menuSide = "LEFT";
                selectingHand = leftHand;
                return;
            }
        }

        private void rightMenuSide_Click(object sender, EventArgs e)
        {
            if (menuSide == "RIGHT")
            {
                return;
            }
            else
            {
                toggleMenuSide();
                menuSide = "RIGHT";
                selectingHand = rightHand;
                return;
            }
        }

        private void toggleMenuSide()
        {
            GridLength curfirst = MainCanvas.ColumnDefinitions.First().Width;
            MainCanvas.ColumnDefinitions.First().Width = MainCanvas.ColumnDefinitions.Last().Width;
            MainCanvas.ColumnDefinitions.Last().Width = curfirst;

            foreach (UIElement c in MainCanvas.Children)
            {
                // Don't move the kinect sensor chooser or the hand canvas
                if (c != kinectSensorChooser && c != Canvas)
                {
                    int newColumn = (Convert.ToInt32(c.GetValue(Grid.ColumnProperty).ToString()) + 1) % 2;
                    c.SetValue(Grid.ColumnProperty, newColumn);
                }
            }

            leftMenuSideButton.highlightButton();
            rightMenuSideButton.highlightButton();
        }

        private void streetMapType_Click(object sender, EventArgs e)
        {
            if (mapType == "ROADMAP") { return; }
            mapType = "ROADMAP";
            changeMapType();
        }

        private void satelliteMapType_Click(object sender, EventArgs e)
        {
            if (mapType == "SATELLITE") { return; }
            mapType = "SATELLITE";
            changeMapType();
            
        }

        private void changeMapType()
        {
            if (mapType == "SATELLITE")
            {
                RIT_Map.ExecuteJavascript(" map.overlayMapTypes.setAt(0, null)");
                RIT_Map.ExecuteJavascript(" map.setMapTypeId(google.maps.MapTypeId.SATELLITE)");
                leftHand.SetLeftWhite();
                rightHand.SetRightWhite();
            }
            else
            {
                RIT_Map.ExecuteJavascript(" map.overlayMapTypes.insertAt(0, tileMapType)");
                RIT_Map.ExecuteJavascript(" map.setMapTypeId(google.maps.MapTypeId.ROADMAP)");
                leftHand.SetLeftBlack();
                rightHand.SetRightBlack();
            }
            satelliteSettingButton.highlightButton();
            streetSettingButton.highlightButton();
        }


        private void zoomIn_Click(object sender, EventArgs e)
        {
            RIT_Map.ExecuteJavascript(" map.setZoom( map.getZoom() + 1)");
            zoomLevel.Value++;
        }

        private void zoomOut_Click(object sender, EventArgs e)
        {
            RIT_Map.ExecuteJavascript(" map.setZoom( map.getZoom() - 1)");
            zoomLevel.Value--;
        }


        #region pan actions

        private void panUp_Click(object sender, EventArgs e)
        {
            RIT_Map.ExecuteJavascript(" map.panBy(0," +  -PAN_DIST + ")");
        }

        private void panDown_Click(object sender, EventArgs e)
        {
            RIT_Map.ExecuteJavascript(" map.panBy(0," + PAN_DIST + ")");
        }

        private void panLeft_Click(object sender, EventArgs e)
        {
            RIT_Map.ExecuteJavascript(" map.panBy(" + -PAN_DIST + ",0)");
        }

        private void panRight_Click(object sender, EventArgs e)
        {
            RIT_Map.ExecuteJavascript(" map.panBy(" + PAN_DIST + ",0)");
        }

        //Panning larger distances for swiping
        private void panUp_Swipe(object sender, EventArgs e)
        {
            RIT_Map.ExecuteJavascript(" map.panBy(0," + -SWP_PAN_DIST + ")");
        }

        private void panDown_Swipe(object sender, EventArgs e)
        {
            RIT_Map.ExecuteJavascript(" map.panBy(0," + SWP_PAN_DIST + ")");
        }

        private void panLeft_Swipe(object sender, EventArgs e)
        {
            RIT_Map.ExecuteJavascript(" map.panBy(" + -SWP_PAN_DIST + ",0)");
        }

        private void panRight_Swipe(object sender, EventArgs e)
        {
            RIT_Map.ExecuteJavascript(" map.panBy(" + SWP_PAN_DIST + ",0)");
        }


        #endregion

        void swipe_OnGestureDetected(string obj)
        {
            switch (obj)
            {
                case "SwipeToLeft":
                    panRight_Swipe(null, null);
                    break;

                case "SwipeToRight":
                    panLeft_Swipe(null, null);
                    break;

                case "SwipeUp":
                    panDown_Swipe(null, null);
                    break;

                case "SwipeDown":
                    panUp_Swipe(null, null);
                    break;

                default:
                    break;
            }


        }

        private void help_Click(object sender, EventArgs e)
        {

        }

        #endregion
    }
}
